package org.mspring.platform.dao.query.criterion;

import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.mspring.platform.dao.query.QueryBuilder;
import org.mspring.platform.dao.query.QueryHelper;
import org.mspring.platform.dao.query.convert.Converter;
import org.mspring.platform.dao.query.convert.ConverterManager;

/**
 * @author Gao Youbo
 * @since Jan 31, 2012
 */
public class BetweenExpression implements Criterion {

    private static final long serialVersionUID = -908009219022303986L;
    private String hqlName;
    private String beginParamKey;
    private String endParamKey;
    private Object beginParamValue;
    private Object endParamValue;
    private Class<?> paramClass;

    protected BetweenExpression(String hqlName, String beginParamKey, String endParamKey) {
        this.hqlName = hqlName;
        this.beginParamKey = beginParamKey;
        this.endParamKey = endParamKey;
    }

    protected BetweenExpression(String hqlName, String beginParamKey, String endParamKey, Class<?> paramClass) {
        this(hqlName, beginParamKey, endParamKey);
        this.paramClass = paramClass;
    }

    public BetweenExpression(String hqlName, Object beginParamValue, Object endParamValue) {
        this.hqlName = hqlName;
        this.beginParamValue = beginParamValue;
        this.endParamValue = endParamValue;
    }

    @Override
    public String toHqlString(QueryBuilder builder) {
        if (beginParamValue != null || endParamValue != null) {
            if (beginParamValue != null && endParamValue != null) {
                return buildBetweenHql(builder, beginParamValue, endParamValue);
            } else if (beginParamValue != null) {
                return new SimpleExpression(hqlName, beginParamValue, ">=").toHqlString(builder);
            } else {
                return new SimpleExpression(hqlName, endParamValue, "<=").toHqlString(builder);
            }
        } else {
            Map<String, Object> queryParams = builder.getQueryParams();
            String beginValue = MapUtils.getString(queryParams, beginParamKey);
            String endValue = MapUtils.getString(queryParams, endParamKey);
            if (org.apache.commons.lang3.StringUtils.isBlank(beginValue) && org.apache.commons.lang3.StringUtils.isBlank(endValue)) {
                return null;
            } else if (org.apache.commons.lang3.StringUtils.isBlank(beginValue)) {
                return new SimpleExpression(hqlName, endParamKey, "<=", paramClass).toHqlString(builder);
            } else if (org.apache.commons.lang3.StringUtils.isBlank(endValue)) {
                return new SimpleExpression(hqlName, beginParamKey, ">=", paramClass).toHqlString(builder);
            } else {
                return buildBetweenHql(builder, beginValue, endValue, paramClass);
            }
        }
    }

    protected String buildBetweenHql(QueryBuilder builder, String beginValue, String endValue, Class<?> paramClass) {
        Converter converter = ConverterManager.lookup(paramClass);

        builder.getNamedQueryParams().put(QueryHelper.qualifyHql(hqlName, "beginParamKey_"), converter.convert(paramClass, beginValue));
        builder.getNamedQueryParams().put(QueryHelper.qualifyHql(hqlName, "endParamKey_"), converter.convert(paramClass, endValue));

        builder.getValidQueryParams().put(beginParamKey, beginValue);
        builder.getValidQueryParams().put(endParamKey, endValue);

        return new StringBuilder().append(hqlName).append(" between ").append(":").append(QueryHelper.qualifyHql(hqlName, "beginParamKey_")).append(" and ").append(":").append(QueryHelper.qualifyHql(hqlName, "endParamKey_")).toString();
    }

    protected String buildBetweenHql(QueryBuilder builder, Object beginValue, Object endValue) {
        builder.getNamedQueryParams().put(QueryHelper.qualifyHql(hqlName, "beginParamKey_"), beginValue);
        builder.getNamedQueryParams().put(QueryHelper.qualifyHql(hqlName) + "endParamKey_", endValue);

        builder.getValidQueryParams().put(beginParamKey, beginValue);
        builder.getValidQueryParams().put(endParamKey, endValue);

        return new StringBuilder().append(hqlName).append(" between ").append(":").append(QueryHelper.qualifyHql(hqlName, "beginParamKey_")).append(" and ").append(":").append(QueryHelper.qualifyHql(hqlName, "endParamKey_")).toString();
    }

}
